﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using CrossEntropyProject.Cross_Entropy;

namespace CrossEntropyProject
{
    public static class DataSet
    {

     #region Class variables

        //  data structure variables for the hour clustering phase

        internal static Hashtable customersHashTable ;  //holds the original customer data (key = id , value = int [] array)
        internal static Int32 numOfCustomers = 0;  //the number of customers in the file
        internal static MainForm mainForm;
        internal static bool  loadingCancelled = false; //flag to indicate if the input file loadinf was cancelled
        internal static int[] arrCallsMax  ; // array that holds the maximum number of calls made by each customer

        //  data structure variables for the cross-entropy phase
 
        internal static Hashtable transformedCustomersHashtable;//holds the transformed data as hashTable 
        internal static double[][] transformedCustomers;//holds the transformed customer vectores
        internal static int[] customerIDarr;//holds the transformed customer ID's

        internal static int[][] hourClusters;

     #endregion


     #region PerformDataTransformation()
        internal static void PerformDataTransformation()
        {
            ArrayList[] clustersArr = PAM.clustersArr;
            ArrayList Medoids = PAM.Medoids;

            transformedCustomersHashtable = new Hashtable();

            int[] cluster;
            hourClusters = new int[clustersArr.Length][];//array of hour clusters (with medoids)

            #region merge the medoid and the cluster to one array, then put it in clusters[i]
            for (int i = 0; i < clustersArr.Length; i++)
            {
                int[] arrptr;
                int j;
                arrptr = clustersArr[i].Cast<int>().ToArray();
                cluster = new int[(arrptr.Length) + 1];// +1 is for the medoid

                for (j = 0; j < arrptr.Length; j++)
                {
                    cluster[j] = arrptr[j];
                }

                cluster[j] = (int)Medoids[i];

                Array.Sort(cluster);
                hourClusters[i] = cluster;
            }
            #endregion

            #region perform data transformation and put the transformed customer to the DataSet.transformedCustomersHashtable

            int[] customer;
            int totalNumOfCallsForCustomer;

            DataSet.transformedCustomers = new double[customersHashTable.Count][];
            DataSet.customerIDarr = new int[transformedCustomers.Length];

            int t = 0;
            foreach (DictionaryEntry entry in DataSet.customersHashTable)//run on all the customer lines in the hashTable
            {
                double[] callsPerCluster = new double[clustersArr.Length];
                customer = ((int[])entry.Value);
                totalNumOfCallsForCustomer = customer.Skip(1).Take(24).Sum();//take the sum between index 1 and 24

                for (int i = 0; i < hourClusters.Length; i++)//run on all hour clusters
                {
                    for (int k = 0; k < hourClusters[i].Length; k++)//run on each object of the cluster
                    {
                        callsPerCluster[i] += (float)customer[hourClusters[i][k]];

                    }
                    callsPerCluster[i] = callsPerCluster[i] / totalNumOfCallsForCustomer;//
                }
               transformedCustomersHashtable.Add(customer[25], callsPerCluster);//hashtable.add(key , value)
               customerIDarr[t] = customer[25];//customerIDarr[t] = key
               transformedCustomers[t] = new double[callsPerCluster.Length];//transformedCustomers[t] = value
               Array.Copy(callsPerCluster, transformedCustomers[t], callsPerCluster.Length);
               t++;
            }
            #endregion



            /**********************************for testing *************************************************************************** */
            //String str = "";
            //StreamWriter sw = new StreamWriter("C:\\Users\\Roman\\Desktop\\transformed.txt");

            //for (int i = 0; i < clusters.Length; i++)
            //{
            //    for (int j = 0; j < clusters[i].Length; j++)
            //    {
            //        str += " "+clusters[i][j].ToString();
            //    }
            //    sw.WriteLine("hour cluster ("+(i+1).ToString()+") :"+str);
            //    str = "";
            //}

            //sw.WriteLine("====================================================");
            //sw.WriteLine("");
            //sw.WriteLine("Number of customers in input file (without zero lines) : " + numOfCustomers.ToString());
            //sw.WriteLine("");
            //sw.WriteLine("");


            //String tmpStr = "";
            //double[] arrPtr;
            //foreach (DictionaryEntry entry in transformedCustomersHashtable)
            //{
            //    arrPtr = (double[]) entry.Value;

            //    for (int i = 0; i < arrPtr.Length; i++)
            //    {
            //        tmpStr += Math.Round(arrPtr[i], 2, MidpointRounding.AwayFromZero) + "  ";
            //    }


            //    str = "id(" + ((int) entry.Key).ToString() + ") transformed vector : < " + tmpStr +
            //        ">  ;  original vector : < " +  String.Join(",",((int[])customersHashTable[((int)entry.Key)]).Skip(1).Take(24)) + " >";
            //    sw.WriteLine(str);
            //    str = "";
            //    tmpStr = "";
            //}
            //sw.Close();
            /* **********************end test region******************************************/
        }
        #endregion


     #region ReadInputFile()
        /// <summary>
        /// This method responsible for reading and storing 
        /// the dataset from the input file.
        /// </summary>
        internal static void ReadInputFile()
        {
            try
            {
                arrCallsMax = new int[Constants.numOfhoursInDay];
                customersHashTable = new Hashtable();

                StreamReader reader = new StreamReader(Constants.inputFileName);
                String line = "";

                Int64 numOfLinesInFile = File.ReadLines(Constants.inputFileName).Count();
                Int64 numOfLinesRead = 0;
                int ProgressPercentage = 0;

                double Read;
                double Total;

                while ((line = reader.ReadLine()) != null)//read the input file until the end
                {
                    StringToArray(ref customersHashTable, line);
                    numOfLinesRead ++;
                    Read = (double)numOfLinesRead;
                    Total = (double)numOfLinesInFile;
                    
                    ProgressPercentage = (int)((Read / Total) * 100);
                  
                //check if backgroundWorker was cancelled
                    if (mainForm.backgroundWorker1.CancellationPending)
                    {
                        Clear();
                        loadingCancelled = true;
                        mainForm.backgroundWorker1.ReportProgress(0);
                        break;
                    }
                    if (ProgressPercentage > 100)
                    {
                        ProgressPercentage = 100;
                    }
                    mainForm.backgroundWorker1.ReportProgress(ProgressPercentage);

                  }//while

                reader.Close();
                numOfCustomers = customersHashTable.Count;

            }
            catch (Exception io)
            {
                MessageBox.Show(@"Error reading file : " + io.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            PamMatrixInput.CalcC_D_F(); //calculate Camulative Destribution Function for each hour
            PamMatrixInput.InitPAMmatrix();//create Distance Matrix for PAM

        }//ReadInputFile()
      #endregion


     #region StringToArray()
        /// <summary>
        /// Method for extracting hours array and customer 
        /// id from a string(Line in the input file)
        /// </summary>
        /// <param name="customerTable"></param>
        /// <param name="customer"></param>
        private static void StringToArray(ref Hashtable customerTable, String customer)
        {

            string[] numbers = Regex.Split(customer, @"\D+");
            int[] arrHours = new int[Constants.numOfhoursInDay+1];

            int customerId = 0;
            int i = 1;
            arrHours[0] = -1;//index 0 in the array is not in use

            try
            {
                foreach (String s in numbers)//the input line is represented by array of string
                {
                    if (!string.IsNullOrEmpty(s) && i < Constants.numOfhoursInDay + 1)
                    {
                        arrHours[i] = int.Parse(s);
                        i++;
                    }
                }

                bool zeroesLine = true;
                //check for zero Line 
                for (int j = 1; j < Constants.numOfhoursInDay; j++)
                {
                    if (arrHours[j] != 0)
                    {
                        zeroesLine = false;
                        if (arrHours[j] > arrCallsMax[j]) // Update each column by it's maximum
                        {
                            arrCallsMax[j] = arrHours[j];
                        }
                    }
                }

                if (!zeroesLine) //if the line is not a zero line then put to hashTable
                {
                    customerId = arrHours[Constants.numOfhoursInDay];
                    customerTable.Add(customerId, arrHours);
                }
            }
            catch (Exception ex) // catch excepction of wrong input file format
            {
                 String message = "This file is in the wrong format. " +
                    Environment.NewLine + "Please open input file with the correct format."+
                    Environment.NewLine + "for more information go to Help -> How to use";

                MessageBox.Show(message,"File format error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                mainForm.backgroundWorker1.CancelAsync(); //tell the backgroundWorker to abort job
            }
        }
     #endregion


     #region SaveClusteringResults()
        internal static void SaveClusteringResults()
        {
            //print each cluster to different file

            List<CustomersCluster> clusters = CrossEntropy.clusters;

            long totalNumOfCustomers = 0;
            for (int i = 0; i < clusters.Count; i++)
            {
                totalNumOfCustomers += clusters[i].customers.Count;
            }
            Hashtable customersHashTable = DataSet.customersHashTable;


            String targetPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) +
                                    @"\" + "Cross Entropy Results " + Path.GetFileNameWithoutExtension(Constants.inputFileName);
            System.IO.Directory.CreateDirectory(targetPath);

            Constants.targetPath = targetPath;

            StreamWriter strwrt = new StreamWriter(targetPath + @"\" + "Statistics.txt");
            strwrt.WriteLine("Created at : " + DateTime.Now);
            strwrt.WriteLine("Input File :" + Path.GetFileNameWithoutExtension(Constants.inputFileName));
            strwrt.WriteLine();
            strwrt.WriteLine("Total number of customers in input file: " + totalNumOfCustomers.ToString());
            strwrt.WriteLine();
            string tmp = "";

            for (int i = 0; i < clusters.Count; i++)
            {
                List<int> customersIDs = clusters[i].customersIDs;
                customersIDs.Sort();

                string str = "Cluster " + (i + 1).ToString() + " :";
                tmp = "";
                for (int j = 0; j < str.Length; j++)
                {
                    tmp += "-";
                }
                strwrt.WriteLine(tmp);
                strwrt.WriteLine(str);
                tmp = "";
                for (int j = 0; j < str.Length; j++)
                {
                    tmp += "-";
                }
                strwrt.WriteLine(tmp);
                strwrt.WriteLine("Number of customers in cluster : " + customersIDs.Count.ToString());
                strwrt.WriteLine();
                strwrt.WriteLine("Percent of customers out of total number of customers : " +
                    Math.Round(((double)customersIDs.Count / (double)totalNumOfCustomers) * 100, 2, MidpointRounding.AwayFromZero).ToString() + "%");
                strwrt.WriteLine();
                strwrt.WriteLine("Means : < " + String.Join("  ,  ", Profiles.profilesList[i].meansOfProfile) + " >");
                strwrt.WriteLine();
                strwrt.WriteLine("Standard diviation values : < " + String.Join("  ,  ", Profiles.profilesList[i].stdValues) + " >");
                strwrt.WriteLine();
                str = "";
                StreamWriter sw = new StreamWriter(targetPath + @"\" + "Cluster " + (i + 1).ToString() + ".txt");
                for (int j = 0; j < customersIDs.Count; j++)
                {
                    str = " " + String.Join(" ", ((int[])customersHashTable[customersIDs[j]]).Skip(1).Take(24)) + " " + customersIDs[j];
                    sw.WriteLine(str);
                    str = "";
                }
                sw.Close();

            }//for

            String st = "Hour Clustering :";
            tmp = "";
            for (int j = 0; j < st.Length; j++)
            {
                tmp += "-";
            }
            strwrt.WriteLine(tmp);
            strwrt.WriteLine(st);
            tmp = "";
            for (int j = 0; j < st.Length; j++)
            {
                tmp += "-";
            }
            strwrt.WriteLine(tmp);
            for (int i = 0; i < DataSet.hourClusters.Length; i++)
            {
                strwrt.WriteLine();
                strwrt.WriteLine("h" + (i + 1).ToString() + " : " + String.Join(" , ", DataSet.hourClusters[i]));
            }

            strwrt.Close();
        }
        #endregion


     #region Clear()
        internal static void Clear()
        {
            if (customersHashTable != null)
            {
                customersHashTable.Clear();
                
                numOfCustomers = 0;
            }

            if (transformedCustomersHashtable != null)
            {
                transformedCustomersHashtable.Clear();
            }

            if (arrCallsMax != null)
            {
                Array.Clear(arrCallsMax, 0, arrCallsMax.Length);
            }

            loadingCancelled = false;
        }
        #endregion

    }//class

}//namespace
